package cn.gs.global.server.meta;

// @author gs
import cn.fuzzytalker.numeric.SimpleEquationUtil;
import cn.gs.db.BoolTransaction;
import cn.gs.global.common.meta.IOgnServer;
import cn.gs.global.common.meta.OgnSnapshot;
import cn.gs.global.table.meta.Obj;
import cn.gs.global.table.meta.Ogn;
import cn.gs.global.table.meta.OgnKeepBackup;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import org.hibernate.Query;

public class OgnServer extends IdNameServer<Ogn> implements IOgnServer {

    MetaCore core;

    public OgnServer(MetaCore core) {
        super(Ogn.class, "Ogn", core.getUtil());
        this.core = core;
    }

    @Override
    public boolean addObj(final int ognId, final int objId, final double num) {
        return util.transaction(new BoolTransaction() {

            @Override
            public boolean work() {
                if (0 == ognId) {
                    return true;
                }
                boolean b = false;
                Ogn ogn = util.get(clazz, ognId);
                if (null != ogn) {
                    Obj keep = util.get(Obj.class, objId);
                    b = ogn.add(keep, num);
                    util.saveOrUpdate(ogn);
                }
                return b;
            }
        });
    }

    static double[] coeOfChg(double[] c) {
        double[] r = new double[6];
        r[0] = c[0];
        r[1] = c[1] - c[2] / 2 + c[3] / 3 - c[4] / 4 + c[5] / 5;
        r[2] = c[2] / 2 - c[3] / 2 + c[4] * 11 / 24 - c[5] * 5 / 12;
        r[3] = c[3] / 6 - c[4] / 4 + c[5] * 7 / 24;
        r[4] = c[4] / 24 - c[5] / 12;
        r[5] = c[5] / 120;
        return r;
    }

    static double cntByDay(double[] c, int x) {
        double[] r = coeOfChg(c);
        return SimpleEquationUtil.calc(r, x);
    }

    static int zeroCrossroad(double[] c, int cnt) {
        double[] r = coeOfChg(c);
        r[0] -= cnt;
        double[] solves = SimpleEquationUtil.solve(r);
        if (solves != null) {
            for (int i = 0; i < solves.length; ++i) {
                if (solves[i] > 0) {
                    return (int) Math.ceil(solves[i]);
                }
            }
        }
        return Integer.MAX_VALUE;
    }

    public int[] zeroCrossroad(int ognId, int... objs) {
        return cntCrossroad(ognId, 0, objs);
    }

    public int[] cntCrossroad(int ognId, int cnt, int... objs) {
        int[] ret = new int[objs.length];
        Ogn ogn = util.get(Ogn.class, ognId);
        if (null == ogn) {
            return null;
        }
        for (int i = 0; i < objs.length; ++i) {
            Obj obj = util.get(Obj.class, objs[i]);
            double[] c = ogn.keep(obj).keepChange();
            ret[i] = (int) zeroCrossroad(c, cnt);
        }
        return ret;
    }

    public int[] keepPredict(int day, int ognId, int... objs) {
        int[] ret = new int[objs.length];
        Ogn ogn = util.get(Ogn.class, ognId);
        if (null == ogn) {
            return null;
        }
        for (int i = 0; i < objs.length; ++i) {
            Obj obj = util.get(Obj.class, objs[i]);
            double[] c = ogn.keep(obj).keepChange();
            ret[i] = (int) cntByDay(c, day);
        }
        return ret;
    }

    public int[] cntInFutureDays(int ognId, int objId, int days) {
        Ogn ogn = util.get(Ogn.class, ognId);
        Obj obj = util.get(Obj.class, objId);
        if (null == ogn || null == obj) {
            return null;
        }
        int[] cnts = new int[days + 1];
        double[] chgs = ogn.keep(obj).keepChange();
        cnts[0] = ogn.keep(obj).getCnt();
        for (int i = 1; i <= days; ++i) {
            cnts[i] = (int) (cnts[i - 1] + chgs[1]);
            chgs[1] += chgs[2];
            chgs[2] += chgs[3];
            chgs[3] += chgs[4];
            chgs[4] += chgs[5];
        }
        return cnts;
    }

    @Override
    public double[] getObjChg(int ognId, int objId) {
        Ogn ogn = util.get(clazz, ognId);
        if (null == ogn) {
            return null;
        }
        Obj keep = util.get(Obj.class, objId);
        return ogn.keep(keep).keepChange();
    }

    @Override
    public double[] keepObj(int ognId, int... objId) {
        Ogn ogn = util.get(clazz, ognId);
        if (null == ogn) {
            return null;
        }
        double[] keeps = new double[objId.length];
        for (int i = 0; i < objId.length; ++i) {
            Obj keep = util.get(Obj.class, objId[i]);
            keeps[i] = ogn.count(keep);
        }
        return keeps;
    }

    @Override
    public OgnSnapshot[] snapshot(int month, int... ids) {
        OgnSnapshot[] ss = new OgnSnapshot[ids.length];
        for (int i = 0; i < ids.length; ++i) {
            Ogn ogn = util.get(clazz, ids[i]);
            if (null == ogn) {
                ss[i] = null;
            } else {
                OgnSnapshot s = snapshot(ogn, month);
                ss[i] = s;
            }
        }
        return ss;
    }

    Collection<OgnKeepBackup> callback(Ogn ogn, int month) {
        Query q = util.query("from OgnKeepBackup where ogn=:ogn and month=:month");
        q.setParameter("ogn", ogn);
        q.setParameter("month", month);
        return q.list();
    }

    OgnSnapshot snapshot(Ogn ogn, int endMonth) {
        //get backup
        Collection<OgnKeepBackup> after = callback(ogn, endMonth);
        ////init ids map
        int[] ids = new int[after.size()];
        Map<Obj, Integer> os = new HashMap<>();
        int index = 0;
        for (OgnKeepBackup og : after) {
            ids[index] = og.getObj().getId();
            os.put(og.getObj(), index++);
        }
        //copy data
        long[] exis = new long[ids.length];
        int[] cnts = new int[ids.length];
        for (OgnKeepBackup og : after) {
            int i = os.get(og.getObj());
            exis[i] = og.getExi();
            cnts[i] = og.getCnt();
        }
        OgnSnapshot sn = new OgnSnapshot();
        sn.setOgnId(ogn.getId());
        sn.setSstime(endMonth);
        sn.setIds(ids);
        sn.setCnts(cnts);
        sn.setExis(exis);
        return sn;
    }
}
